package com.ebao.dao.base.impl;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.List;

import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.ebao.dao.base.IBaseJpaDao;

/**
 * @desc 底层实现
 * @author snow
 * @param <T>
 */
public class BaseJpaDaoImpl<T>  implements IBaseJpaDao<T> {
	
	protected transient final Logger log = LoggerFactory.getLogger(getClass());

	@PersistenceContext
	protected EntityManager entityManager;
	protected Class<T> entityClass;

	@SuppressWarnings("unchecked")
	public BaseJpaDaoImpl() {
		Type t = getClass().getGenericSuperclass();
		if(t instanceof ParameterizedType){
			this.entityClass = (Class<T>) ((ParameterizedType) t).getActualTypeArguments()[0];
		}
	}

	public EntityManager getEntityManager() {
		return this.entityManager;
	}

	public void setEntityManager(EntityManager entityManager) {
		this.entityManager = entityManager;
	}
	
	public void save(final T newInstance) {
		try {
			if (newInstance == null) {
				throw new RuntimeException("保存"+ getEntityName(entityClass) +"对象为空!");
			}
			entityManager.persist(newInstance);
		} catch (RuntimeException e) {
			log.error("保存"+ getEntityName(entityClass) +"的对象为空!",e);
			e.printStackTrace();
			throw e;
		}
	}

	public void update(final T transientObject) {
		entityManager.merge(transientObject);
	}
	
	public void delete(final T entity){
		try {
			if (entity == null) throw new RuntimeException("删除"+ getEntityName(entityClass) +"对象为空!");
			Object obj = entityManager.merge(entity);
			entityManager.remove(obj);
			entityManager.flush();
		} catch (RuntimeException e) {
			log.error("删除"+ getEntityName(entityClass) +"的对象失败!",e);
			e.printStackTrace();
			throw e;
		}
	}
	
	public void delete(final T[] entitys){
		for(T t : entitys){
			delete(t);
		}
	}
	
	public void delete(final Collection<T> entitys){
		for(T t : entitys){
			delete(t);
		}
	}
	
	public void deleteById(final Serializable id) {
		try {
			if (id == null) throw new RuntimeException("删除"+ getEntityName(entityClass) + "对象的ID不能为空!");
			T object = entityManager.getReference(entityClass, id);
			entityManager.remove(object);
		} catch (RuntimeException e) {
			log.error("删除"+ getEntityName(entityClass) + "对象的ID不能为空!",e);
			e.printStackTrace();
			throw e;
		}
	}
	
	public T getById(final Serializable id) {
		if (id == null)
			return null;
		return entityManager.find(entityClass, id);
	}
	
	@SuppressWarnings("unchecked")
	public T getByJpql(final String jpql, final Object... objs){
		Query query = entityManager.createQuery(jpql);
		setParas(query, objs);
		try {
			return (T) query.getSingleResult();
		} catch (NoResultException e) {
			return null;
		}
	}
	
	@SuppressWarnings("unchecked")
	public List<T> queryByPageAfterWhere(final String queryStr, final int begin, final int max, final Object... objs) {
		String clazzName = getEntityName(entityClass);
		StringBuffer sb = new StringBuffer("select obj from ");
		sb.append(clazzName).append(" obj where ").append(queryStr);
		Query query = entityManager.createQuery(sb.toString());
		setParas(query, objs);
		if (begin >= 0 && max > 0) {
			query.setFirstResult(begin);
			query.setMaxResults(max);
		}
		return query.getResultList();
	}

	@SuppressWarnings("rawtypes")
	public List query(final String jpql, final int begin, final int max, final Object... objs) {
		Query query = entityManager.createQuery(jpql);
		setParas(query, objs);
		if (begin >= 0 && max > 0) {
			query.setFirstResult(begin);
			query.setMaxResults(max);
		}
		return query.getResultList();
	}

	private void setParas(Query query, final Object... objs) {
		if (objs != null) {
			int parameterIndex = 1;
			for (Object obj : objs) {
				query.setParameter(parameterIndex++, obj);
			}
		}
	}
	
	public int getCount(final String jpql, final Object... objs){
		Query query = entityManager.createQuery(jpql);
		setParas(query, objs);
		return (Integer)query.getSingleResult();
	}

	public int batchUpdate(final String jpql, final Object... objs) {
		Query query = entityManager.createQuery(jpql);
		setParas(query, objs);
		return query.executeUpdate();
	}
	
	public void clear() {
		entityManager.clear();
	}
	
	public void detach(T t) {
		entityManager.detach(t);
	}
	
	public void flush() {
		entityManager.flush();
	}
	
	public int executeNativeGetCount(final String sqlString, final Object... objs){
		Query query = entityManager.createNativeQuery(sqlString);
		setParas(query, objs);
		return (Integer) query.getSingleResult();
	}
	
	public int executeNativeUpdate(final String sqlString, final Object... objs) {
		Query query = entityManager.createNativeQuery(sqlString);
		setParas(query, objs);
		return query.executeUpdate();
	}
	
	@SuppressWarnings("unchecked")
	public T executeNativeQueryOne(final String sqlString, final Object... objs){
		Query query = entityManager.createNativeQuery(sqlString);
		setParas(query, objs);
		try {
			return (T) query.getSingleResult();
		} catch (NoResultException e) {
			return null;
		}
	}
	
	@SuppressWarnings("rawtypes")
	public List executeNativeQuery(final String sqlString, final Object... objs) {
		Query query = entityManager.createNativeQuery(sqlString);
		setParas(query, objs);
		return (List) query.getResultList();
	}

	@SuppressWarnings("rawtypes")
	public List executeNativeQuery(final String sqlString, final int begin, final int max, final Object... objs) {
		Query query = entityManager.createNativeQuery(sqlString);
		setParas(query, objs);
		if (begin >= 0 && max > 0) {
			query.setFirstResult(begin);
			query.setMaxResults(max);
		}
		return query.getResultList();
	}

	/**
	 * 获取实体的名称
	 * @param <E>
	 * @param clazz 实体类
	 * @return
	 */
	protected <E> String getEntityName(Class<E> clazz){
		String entityname = clazz.getSimpleName();
		Entity entity = clazz.getAnnotation(Entity.class);
		if(StringUtils.isNotBlank(entity.name())){
			entityname = entity.name();
		}
		return entityname;
	}
	
}
